<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>圖解Git</title>
  <link rel='stylesheet' type='text/css' href='visual-git-guide.css'>
  <script type="text/javascript" src='visual-git-guide.js'></script>
</head>
<body onload="replace_all_PNGs();">
  <h1 id="top">圖解Git</h1>

  <div id="language-box">
    <a>其他語言:</a>
    <ul>
      <li><a href='index-de.html'>Deutsch</a></li>
      <li><a href='index-en.html'>English</a></li>
      <li><a href='index-es.html'>Español</a></li>
      <li><a href='index-fr.html'>Français</a></li>
      <li><a href='index-it.html'>Italiano</a></li>
      <li><a href='index-ja.html'>日本語</a></li>
      <li><a href='index-ko.html'>한국어</a></li>
      <li><a href='index-ko.html'>Polski</a></li>
      <li><a href='index-pt.html'>Português</a></li>
      <li><a href='index-ru.html'>Русский</a></li>
      <li><a href='index-sk.html'>Slovenčina</a></li>
      <li><a href='index-vi.html'>Tiếng Việt</a></li>
      <li><a href='index-zh-cn.html'>简体中文</a></li>
      <li class="selected">正體中文</li>
    </ul>
  </div>

  <p id="link-to-png">如果圖片不能顯示，試試<a href="?no-svg">非SVG版</a></p>

  <p id="link-to-svg">SVG圖片已被停用
  <a href="index.html">（重新啟用SVG）</a></p>

  <p>此頁圖解git中的最常用命令。如果你稍微理解git的工作原理，這篇文章能夠讓你理解的更透徹。
  如果你想知道這個網站怎樣產生，請前往<a href='http://github.com/MarkLodato/visual-git-guide'>GitHub
    repository</a>。</p>



  <h2 id="contents">正文</h2>
  <ol>
    <li><a href="#basic-usage">基本使用</a></li>
    <li><a href="#conventions">規範</a></li>
    <li><a href="#commands-in-detail">命令詳解</a>
      <ol>
        <li><a href="#diff">Diff</a></li>
        <li><a href="#commit">Commit</a></li>
        <li><a href="#checkout">Checkout</a></li>
        <li><a href="#detached">Detached HEAD(匿名分支提交)</a></li>
        <li><a href="#reset">Reset</a></li>
        <li><a href="#merge">Merge</a></li>
        <li><a href="#cherry-pick">Cherry Pick</a></li>
        <li><a href="#rebase">Rebase</a></li>
      </ol>
    </li>
    <li><a href="#technical-notes">技術說明</a></li>
  </ol>

  <h2 id="basic-usage">基本使用</h2>

  <div class="center"><img src='basic-usage.svg.png'></div>

  <p>上面的四條命令在工作目錄、暫存目錄(也叫做索引)和倉庫之間複製檔案。</p>

  <ul>

    <li><code>git add <em>files</em></code> 把指定檔案放入暫存區域。</li>

    <li><code>git commit</code> 給暫存區域產生快照並提交。</li>

    <li><code>git reset -- <em>files</em></code> 用來撤銷最後一次<code>git add <em>files</em></code>，你也可以用<code>git reset</code>
    撤銷所有暫存區域檔案。</li>

    <li><code>git checkout -- <em>files</em></code> 把檔從暫存區域複製到工作目錄，用來放棄本地修改。</li>

  </ul>

  <p>你可以用 <code>git reset -p</code>, <code>git checkout -p</code>, or
  <code>git add -p</code>進入互動模式。</p>

  <p>也可以跳過暫存區域直接從倉庫取出檔案或者直接提交代碼。</p>

  <div class="center"><img src='basic-usage-2.svg.png'></div>

  <ul>

    <li><code>git commit -a </code> 相當於執行 <tt>git add</tt>
    把所有目前的目錄下的檔加入暫存區域再執行。<tt>git commit</tt>.</li>

    <li><code>git commit <em>files</em></code> 進行一次包含最後一次提交加上工作目錄中檔快照的提交。並且檔被添加到暫存區域。</li>

    <li><code>git checkout HEAD -- <em>files</em></code> 回滾到複製最後一次提交。</li>

  </ul>

  <h2 id="conventions">規範</h2>

  <p>後文中以下面的形式使用圖片。</p>

  <div class="center"><img src='conventions.svg.png'></div>

  <p>綠色的5位元字元表示提交的ID，分別指向父節點。分支用橘色顯示，分別指向特定的提交。目前分支由附在其上的<em>HEAD</em>標識。
這張圖片裡顯示最後5次提交，<em>ed489</em>是最新提交。 <em>master</em>分支指向此次提交，另一個<em>maint</em>分支指向祖父提交節點。</p>

  <h2 id="commands-in-detail">命令詳解</h2>

  <h3 id="diff">Diff</h3>

  <p>有許多種方法查看兩次提交之間的變動。下面是一些範例。</p>

  <div class="center"><img src='diff.svg.png'></div>

  <h3 id="commit">Commit</h3>

  <p>提交時，git用暫存區域的檔建立一個新的提交，並把此時的節點設為父節點。然後把目前分支指向新的提交節點。下圖中，目前分支是<em>master</em>。
在執行命令之前，<em>master</em>指向<em>ed489</em>，提交後，<em>master</em>指向新的節點<em>f0cec</em>並以<em>ed489</em>作為父節點。</p>

  <div class="center"><img src='commit-master.svg.png'></div>

  <p>即便目前分支是某次提交的祖父節點，git會同樣操作。下圖中，在<em>master</em>分支的祖父節點<em>maint</em>分支進行一次提交，產生了<em>1800b</em>。
這樣，<em>maint</em>分支就不再是<em>master</em>分支的祖父節點。此時，<a href='#merge'>合併</a> (或者 <a href='#rebase'>衍合</a>) 是必須的。</p>

  <div class="center"><img src='commit-maint.svg.png'></div>

  <p>如果想更改一次提交，使用  <code>git commit --amend</code>。git會使用與目前提交相同的父節點進行一次新提交，舊的提交會被取消。</p>

  <div class="center"><img src='commit-amend.svg.png'></div>

  <p>另一個例子是<a href="#detached">分離HEAD提交</a>,後文講。</p>

  <h3 id="checkout">Checkout</h3>

  <p>checkout命令用於從歷史提交（或者暫存區域）中拷貝檔到工作目錄，也可用於切換分支。</p>

  <p>當給定某個檔案名（或者打開-p選項，或者檔案名和-p選項同時打開）時，git會從指定的提交中拷貝檔到暫存區域和工作目錄。比如，<code>git checkout HEAD~ foo.c</code>會將提交節點<em>HEAD~</em>(即目前提交節點的父節點)中的<code>foo.c</code>複製到工作目錄並且加到暫存區域中。（如果命令中沒有指定提交節點，則會從暫存區域中拷貝內容。）注意目前分支不會發生變化。</p>

  <div class="center"><img src='checkout-files.svg.png'></div>

  <p>當不指定檔案名，而是給出一個（本地）分支時，那麼<em>HEAD</em>標識會移動到那個分支（也就是說，我們“切換”到那個分支了），然後暫存區域和工作目錄中的內容會和<em>HEAD</em>對應的提交節點一致。新提交節點（下圖中的a47c3）中的所有檔都會被複製（到暫存區域和工作目錄中）；只存在於老的提交節點（ed489）中的文件會被刪除；不屬於上述兩者的檔會被忽略，不受影響。</p>

  <div class="center"><img src='checkout-branch.svg.png'></div>

  <p>如果既沒有指定檔案名，也沒有指定分支名，而是一個標籤、遠端分支、SHA-1值或者是像<em>master~3</em>類似的東西，就得到一個匿名分支，稱作<em>detached HEAD</em>（被分離的<em>HEAD</em>標識）。這樣可以很方便地在歷史版本之間互相切換。比如說你想要編譯1.6.6.1版本的git，你可以執行<code>git checkout v1.6.6.1</code>（這是一個標籤，而非分支名），編譯，安裝，然後切換回另一個分支，比如說<code>git checkout master</code>。然而，當提交操作涉及到“分離的HEAD”時，其行為會略有不同，詳情見在<a href="#detached">下面</a>。</p>

  <div class="center"><img src='checkout-detached.svg.png'></div>

  <h3 id="detached">HEAD標識處於分離狀態時的提交操作</h3>

  <p>當<em>HEAD</em>處於分離狀態（不依附于任一分支）時，提交操作可以正常進行，但是不會更新任何已命名的分支。(你可以認為這是在更新一個匿名分支。)</p>

  <div class="center"><img src='commit-detached.svg.png'></div>

  <p>一旦此後你切換到別的分支，比如說<em>master</em>，那麼這個提交節點（可能）再也不會被引用到，然後就會被放棄掉了。注意這個命令之後就不會有東西引用<em>2eecb</em>。</p>

  <div class="center"><img src='checkout-after-detached.svg.png'></div>

  <p>但是，如果你想保存這個狀態，可以用命令<code>git checkout -b <em>name</em></code>來建立一個新的分支。</p>

  <div class="center"><img src='checkout-b-detached.svg.png'></div>

  <h3 id="reset">Reset</h3>

  <p>reset命令把目前分支指向另一個位置，並且有選擇的變動工作目錄和索引。也用來在從歷史倉庫中複製檔到索引，而不動工作目錄。</p>

  <p>如果不給選項，那麼目前分支指向到那個提交。如果用<code>--hard</code>選項，那麼工作目錄也更新，如果用<code>--soft</code>選項，那麼都不變。</p>

  <div class="center"><img src='reset-commit.svg.png'></div>

  <p>如果沒有給出提交點的版本號，那麼預設用<em>HEAD</em>。這樣，分支指向不變，但是索引會回溯到最後一次提交，如果用<code>--hard</code>選項，工作目錄也同樣。</p>

  <div class="center"><img src='reset.svg.png'></div>

  <p>如果給了檔案名(或者 <code>-p</code>選項), 那麼工作效果和帶檔案名的<a href='#checkout'>checkout</a>差不多，除了索引被更新。</p>

  <div class="center"><img src='reset-files.svg.png'></div>

  <h3 id="merge">Merge</h3>

  <p>merge 命令把不同分支合併起來。合併前，索引必須和目前提交相同。如果另一個分支是目前提交的祖父節點，那麼合併命令將什麼也不做。
  另一種情況是如果目前提交是另一個分支的祖父節點，就會導致<em>fast-forward</em>合併。指向只是簡單的移動，並產生一個新的提交。</p>

  <div class="center"><img src='merge-ff.svg.png'></div>

  <p>否則就是一次真正的合併。預設把目前提交(<em>ed489</em> 如下所示)和另一個提交(<em>33104</em>)以及他們的共同祖父節點(<em>b325c</em>)進行一次<a
    href='http://en.wikipedia.org/wiki/Three-way_merge'>三方合併</a>。結果是先保存目前的目錄和索引，然後和父節點<em>33104</em>一起做一次新提交。
  </p>

  <div class="center"><img src='merge.svg.png'></div>

  <h3 id="cherry-pick">Cherry Pick</h3>

  <p>cherry-pick命令"複製"一個提交節點並在目前的分支做一次完全一樣的新提交。</p>

  <div class="center"><img src='cherry-pick.svg.png'></div>

  <h3 id="rebase">Rebase</h3>

  <p>衍合是合併命令的另一種選擇。合併把兩個父分支合併進行一次提交，提交歷史不是線性的。衍合在目前分支上重演另一個分支的歷史，提交歷史是線性的。
  本質上，這是線性化的自動的 <a href='#cherry-pick'>cherry-pick</a></p>

  <div class="center"><img src='rebase.svg.png'></div>

  <p>上面的命令都在<em>topic</em>分支中進行，而不是<em>master</em>分支，在<em>master</em>分支上重演，並且把分支指向新的節點。注意舊提交沒有被引用，將被回收。</p>

  <p>要限制回滾範圍，使用<code>--onto</code>選項。下面的命令在<em>master</em>分支上重演目前分支從<em>169a6</em>以來的最近幾個提交，即<em>2c33a</em>。</p>

  <div class="center"><img src='rebase-onto.svg.png'></div>

  <p>同樣有<code>git rebase --interactive</code>讓你更方便的完成一些複雜操作，比如放棄、重排、修改、合併提交。沒有圖片體現這些，細節看這裡:<a
    href='http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html#_interactive_mode'>git-rebase(1)</a></p>

  <h2 id="technical-notes">技術說明</h2>

  <p>檔案內容並沒有真正存儲在索引(<em>.git/index</em>)或者提交物件中，而是以blob的形式分別存儲在資料庫中(<em>.git/objects</em>)，並用SHA-1值來校驗。
索引檔用識別碼列出相關的blob檔以及別的資料。對於提交來說，以樹(<em>tree</em>)的形式存儲，同樣用對於的雜湊值識別。樹對應著工作目錄中的資料夾，樹中包含的
樹或者blob物件對應著相應的子目錄和檔案。每次提交都存儲下它的上一級樹的識別碼。</p>

  <p>如果用detached HEAD提交，那麼最後一次提交會被the reflog for HEAD引用。但是過一段時間就失效，最終被回收，與<code>git commit --amend</code>或者<code>git
    rebase</code>很像。</p>

  <hr>

  <p>Copyright &copy; 2010,
    <a href='mailto:lodatom@gmail.com'>Mark Lodato</a>.
  Traditional Chinese translation &copy; 2015,
    <a href='https://github.com/PeterDaveHello'>Peter Dave Hello</a>.
  </p>

  <p><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/3.0/us/"><img alt="" src="https://i.creativecommons.org/l/by-nc-sa/3.0/us/80x15.png"/></a> 本著作系採用<a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/3.0/us/">創用CC 姓名標示-非商業性-相同方式分享3.0 美國授權條款</a>授權。</p>

</body>
</html>
